﻿using System;
using System.Collections.Generic;
using Business.Common.DataDictionary;
using Business.Common.Exception;
using Business.Common.QueryModel;
using Business.Common.Toolkit;
using Business.DataAccess.Contract.Repository.Inventory;
using Business.DataAccess.Repository.Inventory;
using Business.Domain.Inventory;
using Business.Domain.Warehouse;
using Framework.Core.Collections;
using Framework.Core.Exception;
using Framework.Core.Logger;

namespace Business.Component
{
    public class BillManager
    {
        /// <summary>
        /// 并发锁定对象
        /// </summary>
        private static readonly object Locker = new object();

        #region bill number
        /// <summary>
        /// 获取单据号前缀
        /// </summary>
        /// <param name="billType">单据类型枚举</param>
        /// <returns>单据前缀</returns>
        private static string GetBillNumberPrefix(BillType billType)
        {
            switch (billType)
            {
                case BillType.CountBill:
                    {
                        return "CB";
                    }
                case BillType.InboundBill:
                    {
                        return "IB";
                    }
                case BillType.InboundPlan:
                    {
                        return "IP";
                    }
                case BillType.OutboundBill:
                    {
                        return "OB";
                    }
                case BillType.OutboundPlan:
                    {
                        return "OP";
                    }
                case BillType.Package:
                    {
                        return "PG";
                    }
                case BillType.PickBill:
                    {
                        return "PB";
                    }
                case BillType.PutawayBill:
                    {
                        return "TB";
                    }
                case BillType.SortBill:
                    {
                        return "SB";
                    }
                case BillType.TransferBill:
                    {
                        return "TB";
                    }
                case BillType.ReplenishBill:
                    {
                        return "TB";
                    }
            }

            return string.Empty;
        }

        /// <summary>
        /// 获取新单据号
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billType">单据类型枚举</param>
        /// <returns>新单据号</returns>
        public static string GetNewBillNumber(int warehouseId, BillType billType)
        {
            lock (Locker)
            {
                string settingCode = billType.ToString() + "_BillNumber";
                Setting setting = SettingManager.GetSetting(warehouseId, settingCode);

                if (setting == null)
                    setting = SettingManager.CreateSetting(warehouseId, settingCode, "String", "0", string.Empty, 0);

                string currentSn = setting.SettingValue;
                string numberStr = RadixConvertHelper.X2X(currentSn, 36, 10);
                long oldNumber = 0;
                try
                {
                    oldNumber = long.Parse(numberStr);
                }
                catch (Exception ex)
                {
                    LogHelper.WriteExceptionLog(string.Format("单据序列号{0}转换成int类型失败。", numberStr));
                    ExceptionHelper.HandleException(ex, true, false);
                }

                long newNumber = oldNumber + 1;
                string newNumberStr = RadixConvertHelper.X2X(newNumber.ToString(), 10, 36);
                setting.SettingValue = newNumberStr;
                SettingManager.UpdateSetting(warehouseId, settingCode, "String", setting.Remark, 0);

                return GetBillNumberPrefix(billType) + newNumberStr;
            }
        }
        #endregion

        #region confirm bill
        /// <summary>
        /// 审核单据
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billType">单据类型</param>
        /// <param name="billId">单据编号</param>
        /// <param name="userId">用户编号</param>
        /// <returns>审核成功返回true，否则返回false</returns>
        public static bool Confirm(int warehouseId, BillType billType, long billId, int userId)
        {
            switch (billType)
            {
                case BillType.CountBill:
                    {
                        return ConfirmCountBill(warehouseId, billId, userId);
                    }
                case BillType.InboundBill:
                    {
                        return ConfirmInboundBill(warehouseId, billId, userId);
                    }
                case BillType.InboundPlan:
                    {
                        return ConfirmInboundPlan(warehouseId, billId, userId);
                    }
                case BillType.OutboundBill:
                    {
                        return ConfirmOutboundBill(warehouseId, billId, userId);
                    }
                case BillType.OutboundPlan:
                    {
                        return ConfirmOutboundPlan(warehouseId, billId, userId);
                    }
                case BillType.Package:
                    {
                        return false;
                    }
                case BillType.PickBill:
                    {
                        return false;
                    }
                case BillType.PutawayBill:
                    {
                        return ConfirmTransferBill(warehouseId, billId, userId);
                    }
                case BillType.SortBill:
                    {
                        return false;
                    }
                case BillType.TransferBill:
                    {
                        return ConfirmTransferBill(warehouseId, billId, userId);
                    }
                case BillType.ReplenishBill:
                    {
                        return ConfirmTransferBill(warehouseId, billId, userId);
                    }
            }

            return false;
        }

        /// <summary>
        /// 审核入库计划
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planId">入库计划编号</param>
        /// <param name="userId">审核操作员编号</param>
        /// <returns>成功返回true，否则返回false</returns>
        private static bool ConfirmInboundPlan(int warehouseId, long planId, int userId)
        {
            InboundPlan inboundPlan = GetInboundPlan(planId);
            if (inboundPlan == null)
                BusinessExceptionHelper.ThrowBusinessException("4001");

            if (inboundPlan.BillStatus != (int)InboundPlanStatus.Created || inboundPlan.BillStatus != (int)InboundPlanStatus.Modified)
                BusinessExceptionHelper.ThrowBusinessException("4106");

            inboundPlan.Auditor = userId;
            inboundPlan.AuditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
            inboundPlan.BillStatus = (int)InboundPlanStatus.Confirmed;

            var repository = new InboundPlanRepository();
            return repository.Update(inboundPlan);
        }

        /// <summary>
        /// 审核入库单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billId">入库单编号</param>
        /// <param name="userId">操作员</param>
        /// <returns>成功返回true，否则返回false</returns>
        private static bool ConfirmInboundBill(int warehouseId, long billId, int userId)
        {
            InboundBill inboundBill = null;
            try
            {
                inboundBill = GetInboundBill(billId);
                if (inboundBill == null)
                    BusinessExceptionHelper.ThrowBusinessException("4100");

                if (inboundBill.BillStatus != (int)InboundBillStatus.Received ||
                    inboundBill.BillStatus != (int)InboundBillStatus.Modified)
                    BusinessExceptionHelper.ThrowBusinessException("4101");

                // create inventory data
                List<InboundBillDetail> billDetails = GetInboundBillDetail(billId);
                foreach (InboundBillDetail inboundBillDetail in billDetails)
                {
                    StockManager.CreateStock(warehouseId, billId, inboundBillDetail.SkuId, inboundBillDetail.PackId,
                                             inboundBill.ReceiveLocationId, inboundBillDetail.ContainerId,
                                             inboundBillDetail.Qty, inboundBillDetail.BatchNumber);

                    // update inboundplan detail received qty
                    InboundPlanDetail planDetail = GetInboundPlanDetail(inboundBill.PlanId, inboundBillDetail.SkuId,
                                                                        inboundBillDetail.PackId);
                    UpdateInboundPlanReceivedQty(planDetail, inboundBillDetail.Qty);
                }

                // update inboundbill status
                UpdateInboundBillStatus(inboundBill, InboundBillStatus.Confirmed, userId);

                // update inboundplan status
                InboundPlan plan = GetInboundPlan(inboundBill.PlanId);
                if (plan == null)
                    BusinessExceptionHelper.ThrowBusinessException("4001");

                bool isFullReceived = IsFullReceivedInboundPlan(inboundBill.PlanId);
                InboundPlanStatus inboundPlanStatus;
                if (isFullReceived)
                    inboundPlanStatus = InboundPlanStatus.Received;
                else
                    inboundPlanStatus = InboundPlanStatus.PartialReceived;

                UpdateInboundPlanStatus(plan, inboundPlanStatus, userId);

                return true;
            }
            catch (Exception ex)
            {
                LogHelper.WriteExceptionLog(string.Format("审核入库单{0}失败。", inboundBill.BillNumber));
                ExceptionHelper.HandleException(ex, true, true);
            }

            return false;
        }

        /// <summary>
        /// 审核出库计划
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planId">单据编号</param>
        /// <param name="userId">用户编号</param>
        /// <returns>审核成功返回true，否则返回false</returns>
        private static bool ConfirmOutboundPlan(int warehouseId, long planId, int userId)
        {
            OutboundPlan outboundPlan = GetOutboundPlan(planId);
            if (outboundPlan == null)
                BusinessExceptionHelper.ThrowBusinessException("4401");

            if (outboundPlan.BillStatus != (int)OutboundPlanStatus.Created || outboundPlan.BillStatus != (int)OutboundPlanStatus.Modified)
                BusinessExceptionHelper.ThrowBusinessException("4402");

            outboundPlan.Auditor = userId;
            outboundPlan.AuditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
            outboundPlan.BillStatus = (int)OutboundPlanStatus.Confirmed;

            var repository = new OutboundPlanRepository();
            return repository.Update(outboundPlan);
        }

        /// <summary>
        /// 审核出库单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billId">单据编号</param>
        /// <param name="userId">用户编号</param>
        /// <returns>审核成功返回true，否则返回false</returns>
        private static bool ConfirmOutboundBill(int warehouseId, long billId, int userId)
        {
            //OutboundBill outboundBill = null;
            //try
            //{
            //    outboundBill = GetOutboundBill(billId);
            //    if (outboundBill == null)
            //        BusinessExceptionHelper.ThrowBusinessException("4600");

            //    if (outboundBill.BillStatus != (int)OutboundBillStatus.Created ||
            //        outboundBill.BillStatus != (int)OutboundBillStatus.Modified)
            //        BusinessExceptionHelper.ThrowBusinessException("4601");

            //    // create inventory data
            //    List<InboundBillDetail> billDetails = GetInboundBillDetail(billId);
            //    foreach (InboundBillDetail inboundBillDetail in billDetails)
            //    {
            //        StockManager.CreateStock(warehouseId, billId, inboundBillDetail.SkuId, inboundBillDetail.PackId,
            //                                 inboundBill.ReceiveLocationId, inboundBillDetail.ContainerId,
            //                                 inboundBillDetail.Qty, inboundBillDetail.BatchNumber);

            //        // update inboundplan detail received qty
            //        InboundPlanDetail planDetail = GetInboundPlanDetail(inboundBill.PlanId, inboundBillDetail.SkuId,
            //                                                            inboundBillDetail.PackId);
            //        UpdateInboundPlanReceivedQty(planDetail, inboundBillDetail.Qty);
            //    }

            //    // update inboundbill status
            //    UpdateInboundBillStatus(inboundBill, InboundBillStatus.Confirmed, userId);

            //    // update inboundplan status
            //    InboundPlan plan = GetInboundPlan(inboundBill.PlanId);
            //    if (plan == null)
            //        BusinessExceptionHelper.ThrowBusinessException("4001");

            //    bool isFullReceived = IsFullReceivedInboundPlan(inboundBill.PlanId);
            //    InboundPlanStatus inboundPlanStatus;
            //    if (isFullReceived)
            //        inboundPlanStatus = InboundPlanStatus.Received;
            //    else
            //        inboundPlanStatus = InboundPlanStatus.PartialReceived;

            //    UpdateInboundPlanStatus(plan, inboundPlanStatus, userId);

            //    return true;
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.WriteExceptionLog(string.Format("审核出库单{0}失败。", outboundBill.BillNumber));
            //    ExceptionHelper.HandleException(ex, true, true);
            //}

            return false;
        }

        /// <summary>
        /// 审核盘点单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billId">单据编号</param>
        /// <param name="userId">用户编号</param>
        /// <returns>审核成功返回true，否则返回false</returns>
        private static bool ConfirmCountBill(int warehouseId, long billId, int userId)
        {
            CountBill countBill = GetCountBill(billId);
            if (countBill == null)
                BusinessExceptionHelper.ThrowBusinessException("4500");

            if (countBill.BillStatus != (int)CountBillStatus.Created)
                BusinessExceptionHelper.ThrowBusinessException("4501");

            countBill.Auditor = userId;
            countBill.AuditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
            countBill.BillStatus = (int)CountBillStatus.Confirmed;

            var repository = new CountBillRepository();
            return repository.Update(countBill);
        }

        /// <summary>
        /// 审核移货单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billId">移货单编号</param>
        /// <param name="userId">操作员编号</param>
        /// <returns>成功返回true，否则返回false</returns>
        private static bool ConfirmTransferBill(int warehouseId, long billId, int userId)
        {
            TransferBill transferBill = GetTransferBill(billId);
            if (transferBill == null)
                BusinessExceptionHelper.ThrowBusinessException("4202");

            if (transferBill.BillStatus != (int)TransferBillStatus.Transfered)
                BusinessExceptionHelper.ThrowBusinessException("4103");

            List<TransferBillDetail> billDetails = GetTransferBillDetail(billId);
            foreach (var billDetail in billDetails)
            {
                Stock sourceStock = StockManager.GetStock(warehouseId, billDetail.SourceLocationId,
                                                          billDetail.SourceContainerId, billDetail.SkuId,
                                                          billDetail.PackId, billDetail.BatchNumber);
                if (sourceStock.Qty < billDetail.TransferedQty)
                    BusinessExceptionHelper.ThrowBusinessException("4204");

                Stock newStock = null;
                if (sourceStock.Qty == billDetail.TransferedQty)
                {
                    newStock = StockManager.TransferStock((TransferType)transferBill.TransferType, billId, sourceStock,
                                               billDetail.TargetLocationId, billDetail.SourceContainerId, userId);
                }

                if (sourceStock.Qty > billDetail.TransferedQty)
                {
                    Stock splitStock = StockManager.SplitStock(BillType.TransferBill, billId, sourceStock,
                                                               billDetail.TransferedQty);

                    newStock = StockManager.TransferStock((TransferType)transferBill.TransferType, billId, splitStock,
                                              billDetail.TargetLocationId, billDetail.SourceContainerId, userId);
                }

                if (newStock == null)
                    BusinessExceptionHelper.ThrowBusinessException("4205");
            }

            return true;
        }
        #endregion

        #region inbound plan
        /// <summary>
        /// 获取入库计划
        /// </summary>
        /// <param name="planId">入库计划编号</param>
        /// <returns>成功返回入库计划对象，否则返货null</returns>
        public static InboundPlan GetInboundPlan(long planId)
        {
            var repository = new InboundPlanRepository();
            return repository.Get(planId);
        }

        /// <summary>
        /// 获取入库计划
        /// </summary>
        /// <param name="inboundBill">入库单</param>
        /// <returns>成功返回入库计划对象，否则返货null</returns>
        public static InboundPlan GetInboundPlanByInboundBill(InboundBill inboundBill)
        {
            if (inboundBill == null) return null;

            long planId = inboundBill.PlanId;
            var repository = new InboundPlanRepository();
            return repository.Get(planId);
        }

        /// <summary>
        /// 获取入库计划
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planNumber">入库计划号</param>
        /// <returns>成功返回入库计划对象，否则返货null</returns>
        public static InboundPlan GetInboundPlan(int warehouseId, string planNumber)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("WarehouseId", CriteriaOperator.Equal, warehouseId));
            query.Criteria.Add(new Criterion("BillNumber", CriteriaOperator.Equal, planNumber));

            var repository = new InboundPlanRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 获取入库计划明细列表
        /// </summary>
        /// <param name="planId">入库计划编号</param>
        /// <returns>入库计划明细列表</returns>
        public static List<InboundPlanDetail> GetInboundPlanDetail(long planId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("PlanId", CriteriaOperator.Equal, planId));

            var repository = new InboundPlanDetailRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        /// <summary>
        /// 获取入库计划明细
        /// </summary>
        /// <param name="planId">入库计划编号</param>
        /// <param name="skuId">货物编号</param>
        /// <param name="packId">包装编号</param>
        /// <returns>如果成功返回入库计划明细，否则返回null</returns>
        public static InboundPlanDetail GetInboundPlanDetail(long planId, int skuId, int packId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("PlanId", CriteriaOperator.Equal, planId));
            query.Criteria.Add(new Criterion("SkuId", CriteriaOperator.Equal, skuId));
            query.Criteria.Add(new Criterion("PackId", CriteriaOperator.Equal, packId));

            var repository = new InboundPlanDetailRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 更新入库计划已收货数量
        /// </summary>
        /// <param name="inboundPlanDetail">入库计划明细</param>
        /// <param name="qty">收货数量</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateInboundPlanReceivedQty(InboundPlanDetail inboundPlanDetail, int qty)
        {
            if (inboundPlanDetail != null)
            {
                if ((inboundPlanDetail.ReceivedQty + qty) > inboundPlanDetail.Qty)
                    BusinessExceptionHelper.ThrowBusinessException("4000");

                inboundPlanDetail.ReceivedQty = inboundPlanDetail.ReceivedQty + qty;
                var repository = new InboundPlanDetailRepository();
                return repository.Update(inboundPlanDetail);
            }

            return false;
        }


        /// <summary>
        /// 创建收货任务
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planId">入库计划编号</param>
        /// <param name="deliveryMan">送货员</param>
        /// <param name="vehicle">运输车辆</param>
        /// <param name="arrivalTime">到达时间</param>
        /// <param name="receiveLocationId">收货库位</param>
        /// <param name="remark">备注</param>
        /// <param name="userId">收货员</param>
        /// <returns>成功则返回入库单，否则返回null</returns>
        public static InboundBill CreateReceivingTask(int warehouseId, ref long planId, string deliveryMan,
                                                      string vehicle, string arrivalTime, int receiveLocationId,
                                                      string remark, int userId)
        {
            // get inbound plan
            InboundPlan plan = GetInboundPlan(planId);
            if (plan == null)
                BusinessExceptionHelper.ThrowBusinessException("4001");
            // create inboundbill
            var bill = new InboundBill
                           {
                               ArrivalTime = arrivalTime,
                               BillNumber = GetNewBillNumber(warehouseId, BillType.InboundBill),
                               BillStatus = (int)InboundBillStatus.Receiving,
                               ClientId = plan.ClientId,
                               CreateTime = TypeConvertHelper.DatetimeToString(DateTime.Now),
                               CreateUser = userId,
                               MerchantId = plan.MerchantId,
                               PlanId = planId,
                               ReceiveLocationId = receiveLocationId,
                               Vehicle = vehicle,
                               VendorId = plan.VendorId,
                               WarehouseId = warehouseId,
                               Remark = remark
                           };
            var inboundBillRepository = new InboundBillRepository();
            bill.BillId = inboundBillRepository.Create(bill);
            if (bill.BillId <= 0)
                BusinessExceptionHelper.ThrowBusinessException("4102");
            // create inboundbill aggregate
            List<InboundPlanDetail> planDetails = GetInboundPlanDetail(planId);
            var inboundBillAggregateRepository = new InboundBillAggregateRepository();
            foreach (InboundPlanDetail inboundPlanDetail in planDetails)
            {
                var aggregate = new InboundBillAggregate
                                    {
                                        BillId = bill.BillId,
                                        PackId = inboundPlanDetail.PackId,
                                        Qty = inboundPlanDetail.Qty,
                                        SkuId = inboundPlanDetail.SkuId
                                    };
                aggregate.Id = inboundBillAggregateRepository.Create(aggregate);
                if (aggregate.Id <= 0)
                    BusinessExceptionHelper.ThrowBusinessException("4103");
            }

            return bill;
        }

        /// <summary>
        /// 判断入库计划明细是否完成收货
        /// </summary>
        /// <param name="planId">入库计划编号</param>
        /// <returns>完成收货返回true，否则返回false</returns>
        public static bool IsFullReceivedInboundPlan(long planId)
        {
            List<InboundPlanDetail> planDetails = GetInboundPlanDetail(planId);
            foreach (InboundPlanDetail planDetail in planDetails)
            {
                if (planDetail.ReceivedQty != planDetail.Qty)
                    return false;
            }

            return true;
        }

        /// <summary>
        /// 更新入库计划状态
        /// </summary>
        /// <param name="inboundPlan">入库计划</param>
        /// <param name="planStatus">状态</param>
        /// <param name="userId">操作员</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateInboundPlanStatus(InboundPlan inboundPlan, InboundPlanStatus planStatus, int userId)
        {
            if (inboundPlan != null)
            {
                inboundPlan.BillStatus = (int)planStatus;
                inboundPlan.EditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
                inboundPlan.EditUser = userId;

                var repository = new InboundPlanRepository();
                return repository.Update(inboundPlan);
            }

            return false;
        }

        #endregion inbound plan

        #region inbound bill
        /// <summary>
        /// 获取入库单
        /// </summary>
        /// <param name="billId">入库单编号</param>
        /// <returns>成功返回入库单，否则返回null</returns>
        public static InboundBill GetInboundBill(long billId)
        {
            var repository = new InboundBillRepository();
            return repository.Get(billId);
        }

        /// <summary>
        /// 获取入库单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billNumber">入库单号</param>
        /// <returns>成功返回入库单，否则返回null</returns>
        public static InboundBill GetInboundBill(int warehouseId, string billNumber)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("WarehouseId", CriteriaOperator.Equal, warehouseId));
            query.Criteria.Add(new Criterion("BillNumber", CriteriaOperator.Equal, billNumber));

            var repository = new InboundBillRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 获取入库单明细
        /// </summary>
        /// <param name="billId">入库单编号</param>
        /// <returns>成功返回入库单明细</returns>
        public static List<InboundBillDetail> GetInboundBillDetail(long billId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("BillId", CriteriaOperator.Equal, billId));

            var repository = new InboundBillDetailRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        /// <summary>
        /// 更新入库单状态
        /// </summary>
        /// <param name="inboundBill">入库单</param>
        /// <param name="status">状态</param>
        /// <param name="userId">操作员</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateInboundBillStatus(InboundBill inboundBill, InboundBillStatus status, int userId)
        {
            if (inboundBill != null)
            {
                inboundBill.BillStatus = (int)status;
                inboundBill.EditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
                inboundBill.EditUser = userId;

                var repository = new InboundBillRepository();
                return repository.Update(inboundBill);
            }

            return false;
        }

        
        /// <summary>
        /// 获取收货异常明细
        /// </summary>
        /// <param name="billId">入库单</param>
        /// <returns>返回收货异常明细列表</returns>
        public static List<InboundBillException> GetInboundBillException(long billId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("BillId", CriteriaOperator.Equal, billId));

            var repository = new InboundBillExceptionRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        /// <summary>
        /// 创建收货异常
        /// </summary>
        /// <param name="billId">入库单编号</param>
        /// <param name="skuId">货物编号</param>
        /// <param name="packId">包装编号</param>
        /// <param name="qty">异常数量</param>
        /// <param name="reason">异常原因</param>
        /// <returns>成功返回收货异常，否则返回null</returns>
        public static InboundBillException CreateInboundBillException(long billId, int skuId, int packId, int qty,
                                                                      ExceptionReason reason)
        {
            var exception = new InboundBillException
                                {
                                    BillId = billId,
                                    ExceptionReason = (int)reason,
                                    PackId = packId,
                                    Qty = qty,
                                    SkuId = skuId
                                };

            var repository = new InboundBillExceptionRepository();
            exception.Id = repository.Create(exception);
            if (exception.Id > 0)
                return exception;

            return null;
        }

        /// <summary>
        /// 更新收货异常
        /// </summary>
        /// <param name="exception">收货异常</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateInboundBillException(InboundBillException exception)
        {
            var repository = new InboundBillExceptionRepository();
            return repository.Update(exception);
        }

        public static List<InboundBillAggregate> GetInboundBillAggregate(long billId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("BillId", CriteriaOperator.Equal, billId));

            var repository = new InboundBillAggregateRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        public static InboundBillAggregate CreateInboundBillAggregate(long billId, int skuId, int packId, int qty,
                                                                      ExceptionReason reason)
        {
            var exception = new InboundBillAggregate
                                {
                                    BillId = billId,
                                    PackId = packId,
                                    Qty = qty,
                                    SkuId = skuId
                                };

            var repository = new InboundBillAggregateRepository();
            exception.Id = repository.Create(exception);
            if (exception.Id > 0)
                return exception;

            return null;
        }

        public static bool UpdateInboundBillAggregate(InboundBillAggregate aggregate)
        {
            var repository = new InboundBillAggregateRepository();
            return repository.Update(aggregate);
        }

        /// <summary>
        /// 上传入库单明细列表
        /// </summary>
        /// <param name="billDetails">入库单明细列表</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UploadInboundBillDetail(List<InboundBillDetail> billDetails)
        {
            var repository = new InboundBillDetailRepository();
            foreach (InboundBillDetail billDetail in billDetails)
            {
                billDetail.Id = repository.Create(billDetail);
                if (billDetail.Id <= 0)
                    BusinessExceptionHelper.ThrowBusinessException("4105");
            }

            return true;
        }

        #endregion inbound bill

        #region outbound plan
        /// <summary>
        /// 获取出库计划
        /// </summary>
        /// <param name="planId">出库计划编号</param>
        /// <returns>成功返回出库计划，否则返回null</returns>
        public static OutboundPlan GetOutboundPlan(long planId)
        {
            var repository = new OutboundPlanRepository();
            return repository.Get(planId);
        }

        /// <summary>
        /// 获取出库计划
        /// </summary>
        /// <param name="outboundBill">出库单</param>
        /// <returns>成功返回出库计划，否则返回null</returns>
        public static OutboundPlan GetOutboundPlanByOutboundBill(OutboundBill outboundBill)
        {
            if (outboundBill == null) return null;

            long planId = outboundBill.PlanId;
            var repository = new OutboundPlanRepository();
            return repository.Get(planId);
        }

        /// <summary>
        /// 获取出库计划
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planNumber">出库计划号</param>
        /// <returns>成功返回出库计划，否则返回null</returns>
        public static OutboundPlan GetOutboundPlan(int warehouseId, string planNumber)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("WarehouseId", CriteriaOperator.Equal, warehouseId));
            query.Criteria.Add(new Criterion("PlanNumber", CriteriaOperator.Equal, planNumber));

            var repository = new OutboundPlanRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 获取出库计划明细
        /// </summary>
        /// <param name="planId">出库计划编号</param>
        /// <returns>返回出库计划明细列表</returns>
        public static List<OutboundPlanDetail> GetOutboundPlanDetail(long planId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("PlanId", CriteriaOperator.Equal, planId));

            var repository = new OutboundPlanDetailRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        /// <summary>
        /// 获取出库计划明细
        /// </summary>
        /// <param name="planId">出库计划编号</param>
        /// <param name="skuId">货物编号</param>
        /// <param name="packId">包装编号</param>
        /// <returns>成功返回出库计划明细，否则返回null</returns>
        public static OutboundPlanDetail GetOutboundPlanDetail(long planId, int skuId, int packId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("PlanId", CriteriaOperator.Equal, planId));
            query.Criteria.Add(new Criterion("SkuId", CriteriaOperator.Equal, skuId));
            query.Criteria.Add(new Criterion("PackId", CriteriaOperator.Equal, packId));

            var repository = new OutboundPlanDetailRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 为出库计划分配库存
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planId">出库计划编号</param>
        public static void AssignStockForOutboundPlan(int warehouseId, long planId)
        {
            OutboundPlan outboundPlan = null;
            try
            {
                outboundPlan = GetOutboundPlan(planId);
                if (outboundPlan == null)
                    BusinessExceptionHelper.ThrowBusinessException("4400");

                if (outboundPlan.BillStatus != (int)OutboundPlanStatus.Confirmed)
                    BusinessExceptionHelper.ThrowBusinessException("4401");

                var planDetails = GetOutboundPlanDetail(planId);
                foreach (var planDetail in planDetails)
                {
                    AssignStockForOutboundPlanDetail(outboundPlan.WarehouseId, planDetail);
                }

                UpdateOutboundPlanStatus(outboundPlan, OutboundPlanStatus.Assigned, 0);
            }
            catch (Exception ex)
            {
                LogHelper.WriteExceptionLog(string.Format("出库计划{0}分配库存失败。", outboundPlan.PlanNumber));
                ExceptionHelper.HandleException(ex, true, true);
            }
        }


        /// <summary>
        /// 为出库计划明细分配库存
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="planDetail">出库计划明细</param>
        private static void AssignStockForOutboundPlanDetail(int warehouseId, OutboundPlanDetail planDetail)
        {
            bool assignResult = StockManager.AssignStock(warehouseId, planDetail.PlanId, planDetail.SkuId, planDetail.PackId, planDetail.BatchNumber, planDetail.Qty);
            if (!assignResult)
            {
                string message = string.Format("仓库{0}：为出库计划明细{1}商品{2}包装{3}批次{4}分配{5}库存失败。", warehouseId, planDetail.Id, planDetail.SkuId, planDetail.PackId, planDetail.BatchNumber, planDetail.Qty);
                LogHelper.WriteDebugLog(message);
            }
        }

        /// <summary>
        /// 更新出库计划状态
        /// </summary>
        /// <param name="outboundPlan">出库计划</param>
        /// <param name="planStatus">状态</param>
        /// <param name="userId">操作员</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateOutboundPlanStatus(OutboundPlan outboundPlan, OutboundPlanStatus planStatus, int userId)
        {
            if (outboundPlan != null)
            {
                outboundPlan.BillStatus = (int)planStatus;
                outboundPlan.EditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
                outboundPlan.EditUser = userId;

                var repository = new OutboundPlanRepository();
                return repository.Update(outboundPlan);
            }

            return false;
        }

        #endregion outbound plan

        #region outbound bill
        /// <summary>
        /// 获取出库单
        /// </summary>
        /// <param name="billId">出库单编号</param>
        /// <returns>成功返回出库单，否则返回false</returns>
        public static OutboundBill GetOutboundBill(long billId)
        {
            var repository = new OutboundBillRepository();
            return repository.Get(billId);
        }
        #endregion 

        #region count bill
        /// <summary>
        /// 获取盘点单
        /// </summary>
        /// <param name="billId">盘点单编号</param>
        /// <returns>成功返回盘点单，否则返回null</returns>
        public static CountBill GetCountBill(long billId)
        {
            var repository = new CountBillRepository();
            return repository.Get(billId);
        }

        #endregion 

        #region transfer bill
        /// <summary>
        /// 获取移货单
        /// </summary>
        /// <param name="billId">移货单编号</param>
        /// <returns>成功返回移货单，否则返回null</returns>
        public static TransferBill GetTransferBill(long billId)
        {
            var repository = new TransferBillRepository();
            return repository.Get(billId);
        }

        /// <summary>
        /// 获取移货单
        /// </summary>
        /// <param name="warehouseId">仓库编号</param>
        /// <param name="billNumber">移货单号</param>
        /// <returns>成功返回移货单，否则返回null</returns>
        public static TransferBill GetTransferBill(int warehouseId, string billNumber)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("WarehouseId", CriteriaOperator.Equal, warehouseId));
            query.Criteria.Add(new Criterion("BillNumber", CriteriaOperator.Equal, billNumber));

            var repository = new TransferBillRepository();
            return repository.GetByQuery(query);
        }

        /// <summary>
        /// 获取移货单明细列表
        /// </summary>
        /// <param name="billId">移货单号</param>
        /// <returns>返回移货单明细列表</returns>
        public static List<TransferBillDetail> GetTransferBillDetail(long billId)
        {
            var query = new Query();
            query.Criteria.Add(new Criterion("BillId", CriteriaOperator.Equal, billId));

            var repository = new TransferBillDetailRepository();
            return CollectionHelper.ToList(repository.GetListByQuery(query));
        }

        /// <summary>
        /// 更新移货单状态
        /// </summary>
        /// <param name="transferBill">移货单</param>
        /// <param name="status">状态</param>
        /// <param name="userId">操作员编号</param>
        /// <returns>成功返回true，否则返回false</returns>
        public static bool UpdateTransferBillStatus(TransferBill transferBill, TransferBillStatus status, int userId)
        {
            if (transferBill != null)
            {
                transferBill.BillStatus = (int)status;
                transferBill.EditTime = TypeConvertHelper.DatetimeToString(DateTime.Now);
                transferBill.EditUser = userId;

                var repository = new TransferBillRepository();
                return repository.Update(transferBill);
            }

            return false;
        }

        /// <summary>
        /// 创建移货单
        /// </summary>
        /// <param name="transferBill">移货单</param>
        /// <returns>成功返回移货单，否则返回null</returns>
        public static TransferBill CreateTransferBill(TransferBill transferBill)
        {
            TransferBillRepository transferBillRepository = new TransferBillRepository();
            TransferBillDetailRepository transferBillDetailRepository = new TransferBillDetailRepository();
            if (transferBill != null)
            {
                if (transferBill.Details == null || transferBill.Details.Count <= 0)
                    BusinessExceptionHelper.ThrowBusinessException("4200");

                transferBill.BillNumber = GetNewBillNumber(transferBill.WarehouseId, BillType.TransferBill);
                transferBill.BillId = transferBillRepository.Create(transferBill);
                transferBill.CreateTime = TypeConvertHelper.DatetimeToString(DateTime.Now);

                foreach (var billDetail in transferBill.Details)
                {
                    billDetail.BillId = transferBill.BillId;
                    billDetail.Id = transferBillDetailRepository.Create(billDetail);
                    if (billDetail.Id <= 0)
                        BusinessExceptionHelper.ThrowBusinessException("4201");
                }

                return transferBill;
            }

            return null;
        }
        #endregion transfer bill
    }
}